﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
#if NETFX_CORE
using Windows.UI.Xaml.Navigation;
#else
using System.Net;
using System.Windows.Navigation;
#endif
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;

// ReSharper disable CheckNamespace
namespace MugenMvvmToolkit.ViewModels
// ReSharper restore CheckNamespace
{
    /// <summary>
    ///     Represents the extensions methods for navigation system.
    /// </summary>
    public static class NavigationExtensions
    {
        #region Fields

#if WPF
        private static readonly ConstructorInfo NavigationEventArgsConstructor =
            typeof(NavigationEventArgs).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[]
            {
                typeof (Uri),
                typeof (object),
                typeof (object),
                typeof (WebResponse),
                typeof (object),                
                typeof (bool)
            }, null);

        private static readonly ConstructorInfo NavigatingCancelEventArgsConstructor =
            typeof(NavigatingCancelEventArgs).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[]
            {
                typeof (Uri),
                typeof (object),
                typeof (CustomContentState),
                typeof (object),
                typeof (NavigationMode),
                typeof (WebRequest),
                typeof (object),
                typeof (bool)
            }, null);
#endif


        #endregion

        #region Methods

#if SILVERLIGHT
        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull]IViewModel viewModel, params UriParameter[] uriParameters)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, uriParameters.GetUriParameters());
        }

        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull] IViewModel viewModel, [CanBeNull] string viewName, params UriParameter[] uriParameters)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, uriParameters.GetUriParameters(), viewName);
        }
#endif

#if NETFX_CORE
        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull]IViewModel viewModel, object parameter = null, string viewName = null)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, parameter, viewName);
        }
#endif

#if WPF
        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull]IViewModel viewModel, params UriParameter[] uriParameters)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, uriParameters.GetUriParameters(), null);
        }

        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull]IViewModel viewModel, [CanBeNull]object parameter, params UriParameter[] uriParameters)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, uriParameters.GetUriParameters(), parameter);
        }

        /// <summary>
        ///     Navigates to the specified <see cref="IViewModel" />.
        /// </summary>
        public static INavigationResult Navigate([NotNull]this INavigationProvider navigationProvider, [NotNull] IViewModel viewModel, [CanBeNull]object parameter, [CanBeNull] string viewName, params UriParameter[] uriParameters)
        {
            Should.NotBeNull(navigationProvider, "navigationProvider");
            Should.NotBeNull(viewModel, "viewModel");
            viewModel.NotBeDisposed();
            return navigationProvider.Navigate(viewModel, uriParameters.GetUriParameters(), parameter, viewName);
        }

        /// <summary>
        /// Creates an instance of <see cref="NavigationEventArgs"/>
        /// </summary>
        public static NavigationEventArgs CreateNavigationEventArgs(Uri uri, object content, object extraData, WebResponse response, object navigator, bool isNavigationInitiator)
        {
            return (NavigationEventArgs)NavigationEventArgsConstructor.Invoke(new[] { uri, content, extraData, response, navigator, isNavigationInitiator });
        }

        /// <summary>
        /// Creates an instance of <see cref="NavigatingCancelEventArgs"/>
        /// </summary>
        public static NavigatingCancelEventArgs CreateNavigatingCancelEventArgs(Uri uri, object content, CustomContentState customContentState, object extraData, NavigationMode navigationMode, WebRequest request, object navigator, bool isNavInitiator)
        {
            return (NavigatingCancelEventArgs)NavigatingCancelEventArgsConstructor
                .Invoke(new[] { uri, content, customContentState, extraData, navigationMode, request, navigationMode, isNavInitiator });
        }
#endif
        #endregion
    }
}
